Hey there.
Welcome back.
In this lecture we're going to discuss a critical change that has occurred regarding the RRB script
signals in the Roblox game engine.
Recently, Roblox has been changing how callback functions that are connected to events execute when
an event is fired in the original model.
When an event was fired, any callback functions attached to that event would immediately execute afterwards.
For example, if a bindable event was fired, the threat of execution would go and hop in and execute
any callback functions connected to that event.
This is known as an immediate event.
However, Roblox has now changed it to where when an event is fired, instead of going and executing
the callbacks immediately, they instead are placed in a queue based on when the events were fired,
and then these callbacks are executed all at once in order at a slightly later time in a given frame.
This is why they are called deferred events.
By default, all new created games on Roblox are going to have deferred events enabled.
You can change this property in the workspace and this property is called signal behavior.
The default here is still set to immediate, but this is expected to change in February of 2024, when
the default signal behavior is going to be set to deferred.
The reason why this is such a big change is because it may affect event listeners in your game, if
they have been set up incorrectly.
And I'm going to demonstrate to you what may happen in three different scenarios.
Here in this script, I have set up three different examples of how your scripts may execute differently
based on whether you have signal behavior set to deferred or immediate.
In this first example here I have a function connected to the heartbeat event.
And once we hit the 100th frame because we're just going to increment the current frame one by one,
once we hit the 100th frame, we're going to fire a Bindable event, which is Bindable event A, and
then we're going to fire Bindable event B, and then down here I've connected a handler or a callback
function to Bindable event A, and this function is going to connect another callback to Bindable event
B.
Now, how this would work with immediate events is that when we fire Bindable event A, it's going to
immediately hop in and it's going to execute all of the callbacks that are attached to the, um, event
B script signal for Bindable event A.
So that means this function right here is going to be executed immediately once we call the fire function.
And that means we're going to connect another function to bindable event B.
And this function is going to be executed when bindable event B is fired.
So the thread of execution goes in, it fires this event.
Then it goes and executes this function, attaching another function to bindable event B, and then
it comes back into this function connected to heartbeat.
And then it fires bindable Event B's event.
And that means because we have connected this callback right here, this print statement will execute.
Now with deferred events enabled.
This will never execute right here.
And this is why.
So with deferred events it's going to encounter this statement here.
And it's going to fire the event for Bindable a.
However it's not going to go and execute the function connected to Bindable event A instead it's going
to place it in a queue.
Afterward, it's going to immediately fire the event for Bindable B and any functions that are connected
to bindable B are also going to be placed in a queue.
However, because we do not connect any functions to bindable b until the handler for bindable A executes,
that means nothing's going to happen here, because there are no callbacks connected to B, it goes
fires A, it fires B, it places any functions in a queue for A, and then it places any functions in
a queue for b.
Since B will not have any functions in queue, it's just going to place nothing in queue.
And to prove this, we're going to set signal behavior to immediate and we're going to play the game.
We should see this first print statement show up.
And we should also see the second print statement show up.
So if we go and play the game.
And we look inside of the console.
There we go.
Bindable one callback executed at frame 100 and then Bindable two callback executed at frame 100.
As we'd expected with immediate events, Bindable A was fired.
It connected a callback to Bindable B, and then Bindable B was fired.
And because we connected that callback to bindable B, this print statement executed.
Now if we go and switch the signal behavior to deferred, and then we go and playtest the game.
If we look in our console here, as you can see, only that first print statement showed up Bindable
one callback executed at frame 100.
And as I explained earlier, the reason is, is because any callback functions connected to these events
are placed in a queue.
They aren't executed immediately, and because Bindable B does not have any callback functions attached
to it until the callback function for bindable A gets executed, that means nothing is put in queue
right here.
Let's go ahead and take a look at another example here.
In this example we have three Bindable events and Bindable event A, B and C.
They all have a callback connected to them that simply print out when the callback was executed.
However, with Bindable event a Bindable events A is callback goes and fires the event for bindable
c.
This matches pretty closely with the diagram that we took a look at earlier, where if we have the signal
mode set to immediate, what's going to happen is that when we fire Bindable A, it's going to go in,
it's going to execute this callback, and then it's going to see that Bindable C is fired, and then
Bindable C is fired.
It goes in, it checks to see if Bindable C has any callbacks, which it does, and then it executes
Bindable C's callback.
Afterwards it comes back to this area right here.
And then it fires for Bindable B and that's when Bindable B's callback is going to be executed.
So without deferred events this will execute an a, c, b order.
Now if we switched it with deferred events what's going to happen instead is that Bindable A is going
to fire.
It's going to put this callback in a queue, and it's not going to fire Bindable C until that queue
is reached and it starts executing all of the functions in the queue.
So all the callbacks for bindable A are queued, and then it goes and queues all of the callbacks for
bindable b.
Now once that queue is reached, what's going to happen is the first callback is going to be executed,
and then it's going to fire the event for Bindable C however, it's not going to go and immediately
execute the callback for Bindable C.
Instead, it's going to put the callback for Bindable c in the queue, and since the queue is going
to be in order, that means the callback for bindable C is not going to be executed until after the
callback for Bindable B is executed.
So that means with deferred events, this is going to execute an a, b, c order.
And we can prove that with signal behavior set to deferred and we go and playtest our game.
Let me actually go and comment this out first.
And then we can go play test our game.
If we look Bindable a, B then C executed with signal behavior set to deferred just as we would expect.
But if we go back in the workspace and then we set signal behavior back to immediate.
And then we go and play test our game.
As you can see now, it's set in a C be order because the callback for Bindable A was immediately executed,
which fired the event for Bindable C, that means Bindable C's callback was immediately executed, and
then finally Bindable B's callback was executed.
Okay.
In this final example we're going to take a look at here is we're going to be listening to the character
removing event.
So this gets fired before the character is removed inside of the callback function.
For the character removing event.
We're also attaching another callback function to the ancestry changed event, and we're going to print
when the parent of the character is set to nil.
Now with the media events, all of this should print inside of the console, but with deferred events,
this function right here should never execute for the exact same reason that we observed with the previous
two examples.
Because you think of it internally when the character removing event is going to be fired, any of those
functions connected to it are going to be queued, and then the ancestry changed event is also going
to be fired for the character.
And any of those functions are also going to be placed in a queue.
However, since we're not going to have any functions connected to the ancestry changed event because
they only happen within this callback, that means this function right here should never execute.
So if we go and playtest the game and we have the signal behavior set to immediate, then we should
expect both of those print statements to appear in the console.
So if I go and reset my character and then wait for the character to get removed, there we go.
It says character is removing and then the parent is a nil.
However, if we go and now set the signal behavior to deferred and then we play tested our game.
And then I reset my character.
As you can see now, only character is removing got printed and the parent print statement did not get
printed inside of our console.
I hope this lecture helped clear up any confusion regarding deferred events and immediate events.
Since deferred events are the future for the Roblox game engine, it's important to know how events
are handled to prevent any problems from occurring in your scripts.
Thanks for watching and I'll see you in the next lecture.